#Required libraries
# Tidyverse for data science and exploration
require(dplyr)
Loading required package: dplyr
Attaching package: ‘dplyr’
The following objects are masked from ‘package:stats’:
filter, lag
The following objects are masked from ‘package:base’:
intersect, setdiff, setequal, union
require(tidyr)
Loading required package: tidyr
require(readr)
Loading required package: readr
require(tibble)
Loading required package: tibble
require(stringr)
Loading required package: stringr
require(purrr)
Loading required package: purrr
require(forcats)
Loading required package: forcats
require(rlang)
Loading required package: rlang
Attaching package: ‘rlang’
The following objects are masked from ‘package:purrr’:
%@%, as_function, flatten, flatten_chr, flatten_dbl, flatten_int, flatten_lgl, flatten_raw, invoke, list_along,
modify, prepend, splice
# enhances tidyverse
require(tidylog) # additional logging
Loading required package: tidylog
Attaching package: ‘tidylog’
The following objects are masked from ‘package:tidyr’:
drop_na, fill, gather, pivot_longer, pivot_wider, replace_na, spread, uncount
The following objects are masked from ‘package:dplyr’:
add_count, add_tally, anti_join, count, distinct, distinct_all, distinct_at, distinct_if, filter, filter_all,
filter_at, filter_if, full_join, group_by, group_by_all, group_by_at, group_by_if, inner_join, left_join, mutate,
mutate_all, mutate_at, mutate_if, relocate, rename, rename_all, rename_at, rename_if, rename_with, right_join,
sample_frac, sample_n, select, select_all, select_at, select_if, semi_join, slice, slice_head, slice_max,
slice_min, slice_sample, slice_tail, summarise, summarise_all, summarise_at, summarise_if, summarize,
summarize_all, summarize_at, summarize_if, tally, top_frac, top_n, transmute, transmute_all, transmute_at,
transmute_if, ungroup
The following object is masked from ‘package:stats’:
filter
require(magrittr) # additional data pipe syntax
Loading required package: magrittr
Attaching package: ‘magrittr’
The following object is masked from ‘package:rlang’:
set_names
The following object is masked from ‘package:purrr’:
set_names
The following object is masked from ‘package:tidyr’:
extract
# for reading data in multiple formats
require(readxl)
Loading required package: readxl
require(haven)
Loading required package: haven
# visual analysis
require(ggplot2)
Loading required package: ggplot2
Want to understand how all the pieces fit together? Read R for Data Science: https://r4ds.had.co.nz/
require(GGally) # extensions to ggplot
Loading required package: GGally
Registered S3 method overwritten by 'GGally':
method from
+.gg ggplot2
require(gt) # well formatted tables
Loading required package: gt
# client-side interactive publishable graphics
require(plotly)
Loading required package: plotly
Registered S3 method overwritten by 'data.table':
method from
print.data.table
Registered S3 method overwritten by 'htmlwidgets':
method from
print.htmlwidget tools:rstudio
Attaching package: ‘plotly’
The following object is masked from ‘package:ggplot2’:
last_plot
The following objects are masked from ‘package:tidylog’:
distinct, filter, group_by, mutate, rename, select, slice, summarise, transmute, ungroup
The following object is masked from ‘package:stats’:
filter
The following object is masked from ‘package:graphics’:
layout
require(leaflet)
Loading required package: leaflet
require(crosstalk)
Loading required package: crosstalk
require(htmlwidgets)
Loading required package: htmlwidgets
# server-side interactive graphics
require(shiny)
Loading required package: shiny
Attaching package: ‘shiny’
The following object is masked from ‘package:crosstalk’:
getDefaultReactiveDomain
require(shinyjs)
Loading required package: shinyjs
Find out advanced usage of shinyjs:
https://deanattali.com/shinyjs/advanced
Attaching package: ‘shinyjs’
The following object is masked from ‘package:shiny’:
runExample
The following object is masked from ‘package:gt’:
html
The following objects are masked from ‘package:methods’:
removeClass, show
# Canned Interactive EDA
require(ExPanDaR)
Loading required package: ExPanDaR
Exploring KU Book Processing Charges
# read KU data frame
KUbpc.df <- read_csv("Public Data/openapc-de/data/bpc.csv")
Parsed with column specification:
cols(
institution = col_character(),
period = col_double(),
euro = col_double(),
doi = col_character(),
backlist_oa = col_logical(),
publisher = col_character(),
book_title = col_character(),
isbn = col_character(),
isbn_print = col_character(),
isbn_electronic = col_character(),
license_ref = col_character(),
indexed_in_crossref = col_logical(),
doab = col_logical()
)
# read DOAB metadata
source('Public Data/DOAB/doabingest.R')
DOABmeta.df <- doabFetch()
embedded nul(s) found in input
head(KUbpc.df)
head(summary(KUbpc.df))
institution period euro doi backlist_oa publisher book_title
Length:938 Min. :2017 Min. :1075 Length:938 Mode :logical Length:938 Length:938
Class :character 1st Qu.:2017 1st Qu.:1875 Class :character FALSE:357 Class :character Class :character
Mode :character Median :2018 Median :1981 Mode :character TRUE :581 Mode :character Mode :character
Mean :2018 Mean :4368
3rd Qu.:2019 3rd Qu.:8250
Max. :2020 Max. :8978
isbn isbn_print isbn_electronic license_ref indexed_in_crossref doab
Length:938 Length:938 Length:938 Length:938 Mode :logical Mode :logical
Class :character Class :character Class :character Class :character FALSE:127 FALSE:44
Mode :character Mode :character Mode :character Mode :character TRUE :811 TRUE :894
ggplot(data = KUbpc.df, aes(KUbpc.df$institution)) + geom_bar()

ggplot(data = KUbpc.df, aes(KUbpc.df$euro)) + geom_histogram()

General Exploratory Data Analysis
ggplot(data = KUbpc.df) + geom_bar(mapping = aes(x = KUbpc.df$doab))

# Date to Doab
date_doab <- KUbpc.df %>% ggplot(data = KUbpc.df, mapping = aes(x = KUbpc.df$period, colour = KUbpc.df$doab)) + geom_freqpoly(binwidth = 0.1)
ggplotly(date_doab)
# publisher_euro <- KUbpc.df %>%
# ggplot(data = KUbpc.df, mapping = aes(x = KUbpc.df$publisher, colour = KUbpc.df$euro)) + geom_freqpoly(binwidth = 0.1)
# Institution to Euro
institution_euro <- KUbpc.df %>% ggplot(data = KUbpc.df, mapping = aes(x = KUbpc.df$euro)) + geom_freqpoly(mapping = aes(colour = KUbpc.df$institution), binwidth = 500)
ggplotly(institution_euro)
NA
Idea: Publishers vs. Charges
Question: How do the top 25% of publishers divide up charges (in Euro)?
Observation: Charges are grouped around ~2000 Euros and ~8000 Euros.
publisher_counts <- KUbpc.df %>%
group_by(publisher) %>%
tally
tally: now 110 rows and 2 columns, ungrouped
sorted_counts = arrange(publisher_counts, desc(n))
total_n = sum(sorted_counts$n)
quarter_n = 0.25 * total_n
new_n = sum(sorted_counts$n[0:6])
sorted_counts %>% filter(n > 24)
# filtered <- filter(KUbpc.df$publisher %in% sorted_counts$publisher)
filtered <- filter(KUbpc.df, KUbpc.df$publisher == 'transcript Verlag' |
KUbpc.df$publisher == 'Duke University Press' |
KUbpc.df$publisher == 'University of Michigan Press' |
KUbpc.df$publisher == 'Manchester University Press' |
KUbpc.df$publisher == 'Pluto Press' |
KUbpc.df$publisher == 'Liverpool University Press')
head(filtered)
euro_publisher <- filtered %>%
ggplot(data = filtered, mapping = aes(x = filtered$publisher, y = filtered$euro),
aes(x = filtered$publisher, y = filtered$euro)) +
# geom_count(aes(color = ..n.., size = after_stat(prop), group = euro)) +
geom_count(aes(color = ..n.., group = euro)) +
scale_size_area(max_size = 10) +
theme(axis.text = element_text(size = rel(0.75))) +
labs(title = "How Publishers Divide Charges", x = "Top 25% of Publishers", y = "Price (Euro)", color = 'Number of Copies') +
scale_x_discrete(labels = function(x) str_wrap(str_replace_all(x, "foo", " "), width = 17))
# ggplot:
ggplotly(euro_publisher)
`group_by_()` is deprecated as of dplyr 0.7.0.
Please use `group_by()` instead.
See vignette('programming') for more help
This warning is displayed once every 8 hours.
Call `lifecycle::last_warnings()` to see where this warning was generated.
# crosstalk:
ft <- highlight_key(filtered)
gg_ft <- ggplot(data = ft, mapping = aes(x = filtered$publisher, y = filtered$euro)) +
geom_count(aes(color = ..n.., size = after_stat(prop), group = euro)) +
labs(title = "How Publishers Divide Charges", x = "Top 25% of Publishers", y = "Price (Euro)", color = 'Number of Copies') +
scale_x_discrete(labels = function(x) str_wrap(str_replace_all(x, "foo", " "), width = 17))
cross_ft <- bscols(
filter_select("publisher", "Select a publisher", ft, ~publisher),
ggplotly(gg_ft, dynamicTicks = TRUE),
widths = c(12, 12)
)
All elements of `...` must be named.
Did you want `key = c(key)`?Sum of bscol width units is greater than 12
bscols(cross_ft)
# shared_euro_publisher <- SharedData$new(filtered)
# leaflet(shared_euro_publisher) %>% addMarkers()
# data.table::data.table(shared_euro_publisher)
Idea: Publishers’ Charges vs. Year/OA Type
Sub-Question: What best explains the particular division of charges? (Year, OA Type)
Observation: The low and high charge groups seem to be defined by the type of OA business model, whereas the slight differences within each group seem to be defined by the year.
head(filtered)
# Does Type of OA impact the particular division of charges?
euro_oa_publisher <- filtered %>%
ggplot(data = filtered, mapping = aes(x = filtered$backlist_oa, y = filtered$euro),
aes(x = filtered$backlist_oa, y = filtered$euro)) +
geom_count(aes(color = ..n.., group = euro)) +
scale_size_area(max_size = 10) +
theme(axis.text = element_text(size = rel(0.75))) +
labs(title = "How OA Impacts Price Division of Charges", x = "Type of OA", y = "Price (Euro)", color = 'Number of Copies')
# ggplot:
ggplotly(euro_oa_publisher)
# crosstalk:
ft <- highlight_key(filtered)
gg_ft <- ggplot(data = ft, mapping = aes(x = filtered$backlist_oa, y = filtered$euro)) +
geom_count(aes(color = ..n.., size = after_stat(prop), group = euro)) +
labs(title = "How OA Impacts Division of Charges", x = "Type of OA", y = "Price (Euro)", color = 'Number of Copies')
cross_ft <- bscols(
filter_select("publisher", "Select a publisher", ft, ~publisher),
ggplotly(gg_ft, dynamicTicks = TRUE),
widths = c(12, 12)
)
All elements of `...` must be named.
Did you want `key = c(key)`?Sum of bscol width units is greater than 12
bscols(cross_ft)
# Does Year impact the particular division of charges?
euro_year_publisher <- filtered %>%
ggplot(data = filtered, mapping = aes(x = filtered$period, y = filtered$euro),
aes(x = filtered$period, y = filtered$euro)) +
geom_count(aes(color = ..n.., group = euro)) +
scale_size_area(max_size = 10) +
theme(axis.text = element_text(size = rel(0.75))) +
labs(title = "How Year Impacts Price Division of Charges", x = "Year", y = "Price (Euro)", color = 'Number of Copies')
# ggplot:
ggplotly(euro_year_publisher)
# crosstalk:
ft <- highlight_key(filtered)
gg_ft <- ggplot(data = ft, mapping = aes(x = filtered$period, y = filtered$euro)) +
geom_count(aes(color = ..n.., size = after_stat(prop), group = euro)) +
labs(title = "How Year Impacts Division of Charges", x = "Year", y = "Price (Euro)", color = 'Number of Copies')
cross_ft <- bscols(
filter_select("publisher", "Select a publisher", ft, ~publisher),
ggplotly(gg_ft, dynamicTicks = TRUE),
widths = c(12, 12)
)
All elements of `...` must be named.
Did you want `key = c(key)`?Sum of bscol width units is greater than 12
bscols(cross_ft)
NA
NA
Idea: Publishers vs. OA
Question: What type of business model do the top 25% publishers use?
Observation: Most have a higher proportion of True (moved to OA from traditional publishing) than False (already published OA).
oa_type <- filtered %>%
ggplot(data = filtered, mapping = aes(x = filtered$publisher, colour = filtered$backlist_oa), fill = filtered$backlist_oa) +
geom_bar(position = "fill", width = 0.7, fill="#EAEAEA") +
labs(title = "Business Model OA for Publishers", x = "Top 25% of Publishers", y = "Proportion", color = 'Types of OA') +
theme(axis.text = element_text(size = rel(0.75))) +
scale_x_discrete(labels = function(x) str_wrap(str_replace_all(x, "foo", " "), width = 17)) +
scale_color_brewer(palette = "Set1")
ggplotly(oa_type)
# crosstalk:
ft <- highlight_key(filtered)
oa_ft <- ggplot(data = ft, mapping = aes(x = ft$publisher, colour = ft$backlist_oa), fill = ft$backlist_oa) +
geom_bar(position = "fill", width = 0.7) +
labs(title = "Business Model OA for Publishers", x = "Top 25% of Publishers", y = "Proportion of Backlist OA", color = 'Types of OA') +
theme(axis.text = element_text(size = rel(0.75))) +
scale_x_discrete(labels = function(x) str_wrap(str_replace_all(x, "foo", " "), width = 17))
# cross_oa_ft <- bscols(
# filter_select("publisher", "Select a publisher", ft, ~publisher),
# ggplotly(oa_ft, dynamicTicks = TRUE),
# # widths = c(12, 12)
# )
# bscols(cross_oa_ft)
Idea: Publishers’ OA vs. Year
Question: Did OA business models of the top 25% publishers change per year?
Observation:
oa_time <- function(pub_name) {
pub_ft <- filter(filtered, filtered$publisher == pub_name)
pub_oa <- pub_ft %>%
ggplot(data = pub_ft, mapping = aes(x = pub_ft$period, colour = pub_ft$backlist_oa), fill = pub_ft$backlist_oa) +
geom_bar(position = "fill", width = 0.7, fill="#EAEAEA") +
labs(title = paste(pub_name, "'s OA Through the Years", sep = ""),
x = "Years", y = "Proportion of Backlist OA", color = 'Types of OA') +
theme(axis.text = element_text(size = rel(0.75))) +
scale_x_discrete(limits=c(2017, 2018, 2019)) +
scale_color_brewer(palette = "Set1")
ggplotly(pub_oa)
}
top25_list = c("transcript Verlag", "Duke University Press", "University of Michigan Press", "Manchester University Press", "Pluto Press", "Liverpool University Press")
oa_time("transcript Verlag")
Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?
oa_time("Duke University Press")
Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?
oa_time("University of Michigan Press")
Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?
oa_time("Manchester University Press")
Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?
oa_time("Pluto Press")
Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?
oa_time("Liverpool University Press")
Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?
Idea: Revenue vs. OA
Question: What total revenue are publishers receiving each year?
Observation:
# Finding total revenue for each publisher
revenue_finder <- function(pub_name) {
pub_filtered <- filter(filtered, filtered$publisher == pub_name)
rev = sum(pub_filtered$euro)
}
revenue_df <- data.frame("publisher" = top25_list)
revenue_list <- c()
for (i in top25_list) {
revenue_list<-c(revenue_list,revenue_finder(i))
}
revenue_df$revenue <- c(revenue_list)
print(revenue_df)
# ggplot:
publisher_revenue <- revenue_df %>%
ggplot(data = revenue_df, mapping = aes(x = revenue_df$publisher, y = revenue_df$revenue), fill = revenue_df$revenue) +
geom_col() +
labs(title = "Total Revenue for Publishers", x = "Top 25% of Publishers", y = "Revenue (Euro)", color = 'Types of OA') +
theme(axis.text = element_text(size = rel(0.75))) +
scale_x_discrete(labels = function(x) str_wrap(str_replace_all(x, "foo", " "), width = 17)) +
scale_fill_brewer(palette = "Set1")
ggplotly(publisher_revenue)
Use of `revenue_df$publisher` is discouraged. Use `publisher` instead.Use of `revenue_df$revenue` is discouraged. Use `revenue` instead.
Idea: Revenue vs. OA
Question: What revenue are publishers receiving per year?
Observation:
# Finding total revenue for each publisher
revlist_2017 <- c()
revlist_2018 <- c()
revlist_2019 <- c()
revlist <- c()
for (name in top25_list) {
pub_name <- filter(filtered, filtered$publisher == name)
rev_2017 = sum(pub_name[pub_name$period == 2017,]$euro)
revlist_2017 <- c(revlist_2017, rev_2017)
rev_2018 = sum(pub_name[pub_name$period == 2018,]$euro)
revlist_2018 <- c(revlist_2018, rev_2018)
rev_2019 = sum(pub_name[pub_name$period == 2019,]$euro)
revlist_2019 <- c(revlist_2019, rev_2019)
}
revenue_df <- data.frame("publisher" = top25_list)
revenue_df$'2017' <- c(revlist_2017)
revenue_df$'2018' <- c(revlist_2018)
revenue_df$'2019' <- c(revlist_2019)
print(revenue_df)
revenue_year <- c(revenue_df$'2017', revenue_df$'2018', revenue_df$'2019')
year <- c('2017', '2018', '2019')
# ggplot:
pub_year_revenue1 <- revenue_df %>%
ggplot(data = revenue_df, mapping = aes(x = '2017', y = revenue_df$'2017', fill = revenue_df$publisher)) +
geom_bar(position="dodge", stat="identity") +
labs(title = "Total Revenue for Publishers", x = "Year", y = "Revenue (Euro)", color = 'Publishers') +
theme(axis.text = element_text(size = rel(0.75)))
ggplotly(pub_year_revenue1)
Use of `revenue_df$"2017"` is discouraged. Use `2017` instead.Use of `revenue_df$publisher` is discouraged. Use `publisher` instead.
pub_year_revenue2 <- revenue_df %>%
ggplot(data = revenue_df, mapping = aes(x = '2018', y = revenue_df$'2018', fill = revenue_df$publisher)) +
geom_bar(position="dodge", stat="identity") +
labs(title = "Total Revenue for Publishers", x = "Year", y = "Revenue (Euro)", color = 'Publishers') +
theme(axis.text = element_text(size = rel(0.75)))
ggplotly(pub_year_revenue2)
Use of `revenue_df$"2018"` is discouraged. Use `2018` instead.Use of `revenue_df$publisher` is discouraged. Use `publisher` instead.
pub_year_revenue3 <- revenue_df %>%
ggplot(data = revenue_df, mapping = aes(x = '2019', y = revenue_df$'2019', fill = revenue_df$publisher)) +
geom_bar(position="dodge", stat="identity") +
labs(title = "Total Revenue for Publishers", x = "Year", y = "Revenue (Euro)", color = 'Publishers') +
theme(axis.text = element_text(size = rel(0.75)))
ggplotly(pub_year_revenue3)
Use of `revenue_df$"2019"` is discouraged. Use `2019` instead.Use of `revenue_df$publisher` is discouraged. Use `publisher` instead.
Continued, tried putting it into one graph.
revlist <- c()
revlist_2017 <- c()
revlist_2018 <- c()
revlist_2019 <- c()
for (name in top25_list) {
pub_name <- filter(filtered, filtered$publisher == name)
rev_2017 = sum(pub_name[pub_name$period == 2017,]$euro)
revlist_2017 <- c(revlist_2017, rev_2017)
rev_2018 = sum(pub_name[pub_name$period == 2018,]$euro)
revlist_2018 <- c(revlist_2018, rev_2018)
rev_2019 = sum(pub_name[pub_name$period == 2019,]$euro)
revlist_2019 <- c(revlist_2019, rev_2019)
}
revlist <- c(revlist_2017, revlist_2018, revlist_2019)
print(revlist)
[1] 127420 153491 43044 96849 51824 48131 58125 61875 54750 36000 53625 21375 51750 58125 36000 17625 40500 42375
nrev <- matrix(revlist, ncol=6, byrow=TRUE)
colnames(nrev) <- top25_list
rownames(nrev) <- c("2017", "2018", "2019")
nrev <- as.table(nrev)
nrev <- as.data.frame.matrix(nrev)
print(nrev)
#, nrev$`Duke University Press`, nrev$`University of Michigan Press`, nrev$`Pluto Press`, nrev$`Manchester University Press`, nrev$`Liverpool University Press`
pub_year_rev <- nrev %>%
ggplot(data = nrev, mapping = aes(x = c("2017", "2018", "2019"), y = c(nrev$"transcript Verlag"), fill = nrev$publisher)) +
geom_bar(position="dodge", stat="identity") +
labs(title = "Total Revenue for Publishers", x = "Year", y = "Revenue (Euro)", color = 'Publishers') +
theme(axis.text = element_text(size = rel(0.75)))
ggplotly(pub_year_rev)
Use of `nrev$"transcript Verlag"` is discouraged. Use `transcript Verlag` instead.Use of `nrev$publisher` is discouraged. Use `publisher` instead.
Idea: DOAB analysis
Question: What is the average time gap between year of publication and added on date?
Observation:
DOABmeta.df <- filter(DOABmeta.df, is.na(DOABmeta.df$Year.of.publication))
print(DOABmeta.df$Year.of.publication[1:4])
[1] <NA> <NA> <NA> <NA>
694 Levels: ...
gap = mean(DOABmeta.df$Added.on.date - DOABmeta.df$Year.of.publication[1:3])
‘-’ not meaningful for factors
print(gap)
[1] NA
Comparison of charges by year and backlist
Interactive charges exploration
### Interactive Dataset Exploration
LS0tCnRpdGxlOiAiRXhwbG9yYXRvcnkgQW5hbHlzaXMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmBgYHtyfQojUmVxdWlyZWQgbGlicmFyaWVzCgojIFRpZHl2ZXJzZSBmb3IgZGF0YSBzY2llbmNlIGFuZCBleHBsb3JhdGlvbgpyZXF1aXJlKGRwbHlyKQpyZXF1aXJlKHRpZHlyKQpyZXF1aXJlKHJlYWRyKQpyZXF1aXJlKHRpYmJsZSkKcmVxdWlyZShzdHJpbmdyKQpyZXF1aXJlKHB1cnJyKQpyZXF1aXJlKGZvcmNhdHMpCnJlcXVpcmUocmxhbmcpCgojIGVuaGFuY2VzIHRpZHl2ZXJzZQpyZXF1aXJlKHRpZHlsb2cpICMgYWRkaXRpb25hbCBsb2dnaW5nCnJlcXVpcmUobWFncml0dHIpICMgYWRkaXRpb25hbCBkYXRhIHBpcGUgc3ludGF4CgoKIyBmb3IgcmVhZGluZyBkYXRhIGluIG11bHRpcGxlIGZvcm1hdHMKcmVxdWlyZShyZWFkeGwpCnJlcXVpcmUoaGF2ZW4pCgojIHZpc3VhbCBhbmFseXNpcwpyZXF1aXJlKGdncGxvdDIpCnJlcXVpcmUoR0dhbGx5KSAjIGV4dGVuc2lvbnMgdG8gZ2dwbG90CnJlcXVpcmUoZ3QpICMgd2VsbCBmb3JtYXR0ZWQgdGFibGVzCiMgY2xpZW50LXNpZGUgaW50ZXJhY3RpdmUgcHVibGlzaGFibGUgZ3JhcGhpY3MKcmVxdWlyZShwbG90bHkpCnJlcXVpcmUobGVhZmxldCkKcmVxdWlyZShjcm9zc3RhbGspCnJlcXVpcmUoaHRtbHdpZGdldHMpCiMgc2VydmVyLXNpZGUgaW50ZXJhY3RpdmUgZ3JhcGhpY3MKcmVxdWlyZShzaGlueSkKcmVxdWlyZShzaGlueWpzKQojIENhbm5lZCBJbnRlcmFjdGl2ZSBFREEgCnJlcXVpcmUoRXhQYW5EYVIpCgoKYGBgCiMjIEV4cGxvcmluZyBLVSBCb29rIFByb2Nlc3NpbmcgQ2hhcmdlcwpgYGB7ciAgfQojIHJlYWQgS1UgZGF0YSBmcmFtZQpLVWJwYy5kZiA8LSByZWFkX2NzdigiUHVibGljIERhdGEvb3BlbmFwYy1kZS9kYXRhL2JwYy5jc3YiKQojIHJlYWQgRE9BQiBtZXRhZGF0YQoKc291cmNlKCdQdWJsaWMgRGF0YS9ET0FCL2RvYWJpbmdlc3QuUicpCkRPQUJtZXRhLmRmIDwtIGRvYWJGZXRjaCgpCmBgYApgYGB7ciAgfQoKCmhlYWQoS1VicGMuZGYpCmhlYWQoc3VtbWFyeShLVWJwYy5kZikpCgpnZ3Bsb3QoZGF0YSA9IEtVYnBjLmRmLCBhZXMoS1VicGMuZGYkaW5zdGl0dXRpb24pKSArIGdlb21fYmFyKCkgCgpnZ3Bsb3QoZGF0YSA9IEtVYnBjLmRmLCBhZXMoS1VicGMuZGYkZXVybykpICsgZ2VvbV9oaXN0b2dyYW0oKQoKYGBgCiMjIEdlbmVyYWwgRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcwpgYGB7ciAgfQoKZ2dwbG90KGRhdGEgPSBLVWJwYy5kZikgKyBnZW9tX2JhcihtYXBwaW5nID0gYWVzKHggPSBLVWJwYy5kZiRkb2FiKSkKCiMgRGF0ZSB0byBEb2FiCmRhdGVfZG9hYiA8LSBLVWJwYy5kZiAlPiUgZ2dwbG90KGRhdGEgPSBLVWJwYy5kZiwgbWFwcGluZyA9IGFlcyh4ID0gS1VicGMuZGYkcGVyaW9kLCBjb2xvdXIgPSBLVWJwYy5kZiRkb2FiKSkgKyBnZW9tX2ZyZXFwb2x5KGJpbndpZHRoID0gMC4xKQpnZ3Bsb3RseShkYXRlX2RvYWIpCgojIHB1Ymxpc2hlcl9ldXJvIDwtIEtVYnBjLmRmICU+JSAKIyBnZ3Bsb3QoZGF0YSA9IEtVYnBjLmRmLCBtYXBwaW5nID0gYWVzKHggPSBLVWJwYy5kZiRwdWJsaXNoZXIsIGNvbG91ciA9IEtVYnBjLmRmJGV1cm8pKSArIGdlb21fZnJlcXBvbHkoYmlud2lkdGggPSAwLjEpCgojIEluc3RpdHV0aW9uIHRvIEV1cm8KaW5zdGl0dXRpb25fZXVybyA8LSBLVWJwYy5kZiAlPiUgZ2dwbG90KGRhdGEgPSBLVWJwYy5kZiwgbWFwcGluZyA9IGFlcyh4ID0gS1VicGMuZGYkZXVybykpICsgZ2VvbV9mcmVxcG9seShtYXBwaW5nID0gYWVzKGNvbG91ciA9IEtVYnBjLmRmJGluc3RpdHV0aW9uKSwgYmlud2lkdGggPSA1MDApCgpnZ3Bsb3RseShpbnN0aXR1dGlvbl9ldXJvKQoKYGBgCiMjIElkZWE6IFB1Ymxpc2hlcnMgdnMuIENoYXJnZXMKIyMgUXVlc3Rpb246IEhvdyBkbyB0aGUgdG9wIDI1JSBvZiBwdWJsaXNoZXJzIGRpdmlkZSB1cCBjaGFyZ2VzIChpbiBFdXJvKT8KIyMgT2JzZXJ2YXRpb246IENoYXJnZXMgYXJlIGdyb3VwZWQgYXJvdW5kIH4yMDAwIEV1cm9zIGFuZCB+ODAwMCBFdXJvcy4gCmBgYHtyICB9CgpwdWJsaXNoZXJfY291bnRzIDwtIEtVYnBjLmRmICU+JQogICAgZ3JvdXBfYnkocHVibGlzaGVyKSAlPiUKICAgIHRhbGx5Cgpzb3J0ZWRfY291bnRzID0gYXJyYW5nZShwdWJsaXNoZXJfY291bnRzLCBkZXNjKG4pKQoKdG90YWxfbiA9IHN1bShzb3J0ZWRfY291bnRzJG4pCnF1YXJ0ZXJfbiA9IDAuMjUgKiB0b3RhbF9uCm5ld19uID0gc3VtKHNvcnRlZF9jb3VudHMkblswOjZdKQoKc29ydGVkX2NvdW50cyAlPiUgZmlsdGVyKG4gPiAyNCkKCiMgZmlsdGVyZWQgPC0gZmlsdGVyKEtVYnBjLmRmJHB1Ymxpc2hlciAlaW4lIHNvcnRlZF9jb3VudHMkcHVibGlzaGVyKQoKZmlsdGVyZWQgPC0gZmlsdGVyKEtVYnBjLmRmLCBLVWJwYy5kZiRwdWJsaXNoZXIgPT0gJ3RyYW5zY3JpcHQgVmVybGFnJyB8CiAgICAgICAgICAgICAgICAgICAgIEtVYnBjLmRmJHB1Ymxpc2hlciA9PSAnRHVrZSBVbml2ZXJzaXR5IFByZXNzJyB8CiAgICAgICAgICAgICAgICAgICAgIEtVYnBjLmRmJHB1Ymxpc2hlciA9PSAnVW5pdmVyc2l0eSBvZiBNaWNoaWdhbiBQcmVzcycgfAogICAgICAgICAgICAgICAgICAgICBLVWJwYy5kZiRwdWJsaXNoZXIgPT0gJ01hbmNoZXN0ZXIgVW5pdmVyc2l0eSBQcmVzcycgfAogICAgICAgICAgICAgICAgICAgICBLVWJwYy5kZiRwdWJsaXNoZXIgPT0gJ1BsdXRvIFByZXNzJyB8CiAgICAgICAgICAgICAgICAgICAgIEtVYnBjLmRmJHB1Ymxpc2hlciA9PSAnTGl2ZXJwb29sIFVuaXZlcnNpdHkgUHJlc3MnKQoKaGVhZChmaWx0ZXJlZCkKCmV1cm9fcHVibGlzaGVyIDwtIGZpbHRlcmVkICU+JSAKICBnZ3Bsb3QoZGF0YSA9IGZpbHRlcmVkLCBtYXBwaW5nID0gYWVzKHggPSBmaWx0ZXJlZCRwdWJsaXNoZXIsIHkgPSBmaWx0ZXJlZCRldXJvKSwgCiAgICAgICAgIGFlcyh4ID0gZmlsdGVyZWQkcHVibGlzaGVyLCB5ID0gZmlsdGVyZWQkZXVybykpICsgCiAgIyBnZW9tX2NvdW50KGFlcyhjb2xvciA9IC4ubi4uLCBzaXplID0gYWZ0ZXJfc3RhdChwcm9wKSwgZ3JvdXAgPSBldXJvKSkgKyAKICBnZW9tX2NvdW50KGFlcyhjb2xvciA9IC4ubi4uLCBncm91cCA9IGV1cm8pKSArIAogIHNjYWxlX3NpemVfYXJlYShtYXhfc2l6ZSA9IDEwKSArIAogIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDAuNzUpKSkgKwogIGxhYnModGl0bGUgPSAiSG93IFB1Ymxpc2hlcnMgRGl2aWRlIENoYXJnZXMiLCB4ID0gIlRvcCAyNSUgb2YgUHVibGlzaGVycyIsIHkgPSAiUHJpY2UgKEV1cm8pIiwgY29sb3IgPSAnTnVtYmVyIG9mIENvcGllcycpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGZ1bmN0aW9uKHgpIHN0cl93cmFwKHN0cl9yZXBsYWNlX2FsbCh4LCAiZm9vIiwgIiAiKSwgd2lkdGggPSAxNykpCgojIGdncGxvdDoKZ2dwbG90bHkoZXVyb19wdWJsaXNoZXIpCgojIGNyb3NzdGFsazoKZnQgPC0gaGlnaGxpZ2h0X2tleShmaWx0ZXJlZCkKZ2dfZnQgPC0gZ2dwbG90KGRhdGEgPSBmdCwgbWFwcGluZyA9IGFlcyh4ID0gZmlsdGVyZWQkcHVibGlzaGVyLCB5ID0gZmlsdGVyZWQkZXVybykpICsgCiAgZ2VvbV9jb3VudChhZXMoY29sb3IgPSAuLm4uLiwgc2l6ZSA9IGFmdGVyX3N0YXQocHJvcCksIGdyb3VwID0gZXVybykpICsgCiAgbGFicyh0aXRsZSA9ICJIb3cgUHVibGlzaGVycyBEaXZpZGUgQ2hhcmdlcyIsIHggPSAiVG9wIDI1JSBvZiBQdWJsaXNoZXJzIiwgeSA9ICJQcmljZSAoRXVybykiLCBjb2xvciA9ICdOdW1iZXIgb2YgQ29waWVzJykgKwogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gZnVuY3Rpb24oeCkgc3RyX3dyYXAoc3RyX3JlcGxhY2VfYWxsKHgsICJmb28iLCAiICIpLCB3aWR0aCA9IDE3KSkKY3Jvc3NfZnQgPC0gYnNjb2xzKAogIGZpbHRlcl9zZWxlY3QoInB1Ymxpc2hlciIsICJTZWxlY3QgYSBwdWJsaXNoZXIiLCBmdCwgfnB1Ymxpc2hlciksCiAgZ2dwbG90bHkoZ2dfZnQsIGR5bmFtaWNUaWNrcyA9IFRSVUUpLAogIHdpZHRocyA9IGMoMTIsIDEyKQopCgpic2NvbHMoY3Jvc3NfZnQpCgojIHNoYXJlZF9ldXJvX3B1Ymxpc2hlciA8LSBTaGFyZWREYXRhJG5ldyhmaWx0ZXJlZCkKIyBsZWFmbGV0KHNoYXJlZF9ldXJvX3B1Ymxpc2hlcikgJT4lIGFkZE1hcmtlcnMoKQojIGRhdGEudGFibGU6OmRhdGEudGFibGUoc2hhcmVkX2V1cm9fcHVibGlzaGVyKQoKCmBgYAojIyBJZGVhOiBQdWJsaXNoZXJzJyBDaGFyZ2VzIHZzLiBZZWFyL09BIFR5cGUKIyMgU3ViLVF1ZXN0aW9uOiBXaGF0IGJlc3QgZXhwbGFpbnMgdGhlIHBhcnRpY3VsYXIgZGl2aXNpb24gb2YgY2hhcmdlcz8gKFllYXIsIE9BIFR5cGUpCiMjIE9ic2VydmF0aW9uOiBUaGUgbG93IGFuZCBoaWdoIGNoYXJnZSBncm91cHMgc2VlbSB0byBiZSBkZWZpbmVkIGJ5IHRoZSB0eXBlIG9mIE9BIGJ1c2luZXNzIG1vZGVsLCB3aGVyZWFzIHRoZSBzbGlnaHQgZGlmZmVyZW5jZXMgd2l0aGluIGVhY2ggZ3JvdXAgc2VlbSB0byBiZSBkZWZpbmVkIGJ5IHRoZSB5ZWFyLiAKYGBge3IgIH0KCmhlYWQoZmlsdGVyZWQpCgojIERvZXMgVHlwZSBvZiBPQSBpbXBhY3QgdGhlIHBhcnRpY3VsYXIgZGl2aXNpb24gb2YgY2hhcmdlcz8KCmV1cm9fb2FfcHVibGlzaGVyIDwtIGZpbHRlcmVkICU+JSAKICBnZ3Bsb3QoZGF0YSA9IGZpbHRlcmVkLCBtYXBwaW5nID0gYWVzKHggPSBmaWx0ZXJlZCRiYWNrbGlzdF9vYSwgeSA9IGZpbHRlcmVkJGV1cm8pLCAKICAgICAgICAgYWVzKHggPSBmaWx0ZXJlZCRiYWNrbGlzdF9vYSwgeSA9IGZpbHRlcmVkJGV1cm8pKSArIAogIGdlb21fY291bnQoYWVzKGNvbG9yID0gLi5uLi4sIGdyb3VwID0gZXVybykpICsgCiAgc2NhbGVfc2l6ZV9hcmVhKG1heF9zaXplID0gMTApICsgCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC43NSkpKSArCiAgbGFicyh0aXRsZSA9ICJIb3cgT0EgSW1wYWN0cyBQcmljZSBEaXZpc2lvbiBvZiBDaGFyZ2VzIiwgeCA9ICJUeXBlIG9mIE9BIiwgeSA9ICJQcmljZSAoRXVybykiLCBjb2xvciA9ICdOdW1iZXIgb2YgQ29waWVzJykKCiMgZ2dwbG90OgpnZ3Bsb3RseShldXJvX29hX3B1Ymxpc2hlcikKCiMgY3Jvc3N0YWxrOgpmdCA8LSBoaWdobGlnaHRfa2V5KGZpbHRlcmVkKQpnZ19mdCA8LSBnZ3Bsb3QoZGF0YSA9IGZ0LCBtYXBwaW5nID0gYWVzKHggPSBmaWx0ZXJlZCRiYWNrbGlzdF9vYSwgeSA9IGZpbHRlcmVkJGV1cm8pKSArIAogIGdlb21fY291bnQoYWVzKGNvbG9yID0gLi5uLi4sIHNpemUgPSBhZnRlcl9zdGF0KHByb3ApLCBncm91cCA9IGV1cm8pKSArIAogIGxhYnModGl0bGUgPSAiSG93IE9BIEltcGFjdHMgRGl2aXNpb24gb2YgQ2hhcmdlcyIsIHggPSAiVHlwZSBvZiBPQSIsIHkgPSAiUHJpY2UgKEV1cm8pIiwgY29sb3IgPSAnTnVtYmVyIG9mIENvcGllcycpCmNyb3NzX2Z0IDwtIGJzY29scygKICBmaWx0ZXJfc2VsZWN0KCJwdWJsaXNoZXIiLCAiU2VsZWN0IGEgcHVibGlzaGVyIiwgZnQsIH5wdWJsaXNoZXIpLAogIGdncGxvdGx5KGdnX2Z0LCBkeW5hbWljVGlja3MgPSBUUlVFKSwKICB3aWR0aHMgPSBjKDEyLCAxMikKKQoKYnNjb2xzKGNyb3NzX2Z0KQoKCiMgRG9lcyBZZWFyIGltcGFjdCB0aGUgcGFydGljdWxhciBkaXZpc2lvbiBvZiBjaGFyZ2VzPwoKZXVyb195ZWFyX3B1Ymxpc2hlciA8LSBmaWx0ZXJlZCAlPiUgCiAgZ2dwbG90KGRhdGEgPSBmaWx0ZXJlZCwgbWFwcGluZyA9IGFlcyh4ID0gZmlsdGVyZWQkcGVyaW9kLCB5ID0gZmlsdGVyZWQkZXVybyksIAogICAgICAgICBhZXMoeCA9IGZpbHRlcmVkJHBlcmlvZCwgeSA9IGZpbHRlcmVkJGV1cm8pKSArIAogIGdlb21fY291bnQoYWVzKGNvbG9yID0gLi5uLi4sIGdyb3VwID0gZXVybykpICsgCiAgc2NhbGVfc2l6ZV9hcmVhKG1heF9zaXplID0gMTApICsgCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC43NSkpKSArCiAgbGFicyh0aXRsZSA9ICJIb3cgWWVhciBJbXBhY3RzIFByaWNlIERpdmlzaW9uIG9mIENoYXJnZXMiLCB4ID0gIlllYXIiLCB5ID0gIlByaWNlIChFdXJvKSIsIGNvbG9yID0gJ051bWJlciBvZiBDb3BpZXMnKQoKIyBnZ3Bsb3Q6CmdncGxvdGx5KGV1cm9feWVhcl9wdWJsaXNoZXIpCgojIGNyb3NzdGFsazoKZnQgPC0gaGlnaGxpZ2h0X2tleShmaWx0ZXJlZCkKZ2dfZnQgPC0gZ2dwbG90KGRhdGEgPSBmdCwgbWFwcGluZyA9IGFlcyh4ID0gZmlsdGVyZWQkcGVyaW9kLCB5ID0gZmlsdGVyZWQkZXVybykpICsgCiAgZ2VvbV9jb3VudChhZXMoY29sb3IgPSAuLm4uLiwgc2l6ZSA9IGFmdGVyX3N0YXQocHJvcCksIGdyb3VwID0gZXVybykpICsgCiAgbGFicyh0aXRsZSA9ICJIb3cgWWVhciBJbXBhY3RzIERpdmlzaW9uIG9mIENoYXJnZXMiLCB4ID0gIlllYXIiLCB5ID0gIlByaWNlIChFdXJvKSIsIGNvbG9yID0gJ051bWJlciBvZiBDb3BpZXMnKQpjcm9zc19mdCA8LSBic2NvbHMoCiAgZmlsdGVyX3NlbGVjdCgicHVibGlzaGVyIiwgIlNlbGVjdCBhIHB1Ymxpc2hlciIsIGZ0LCB+cHVibGlzaGVyKSwKICBnZ3Bsb3RseShnZ19mdCwgZHluYW1pY1RpY2tzID0gVFJVRSksCiAgd2lkdGhzID0gYygxMiwgMTIpCikKCmJzY29scyhjcm9zc19mdCkKCgpgYGAKIyMgSWRlYTogUHVibGlzaGVycyB2cy4gT0EKIyMgUXVlc3Rpb246IFdoYXQgdHlwZSBvZiBidXNpbmVzcyBtb2RlbCBkbyB0aGUgdG9wIDI1JSBwdWJsaXNoZXJzIHVzZT8KIyMgT2JzZXJ2YXRpb246IE1vc3QgaGF2ZSBhIGhpZ2hlciBwcm9wb3J0aW9uIG9mIFRydWUgKG1vdmVkIHRvIE9BIGZyb20gdHJhZGl0aW9uYWwgcHVibGlzaGluZykgdGhhbiBGYWxzZSAoYWxyZWFkeSBwdWJsaXNoZWQgT0EpLgpgYGB7ciAgfQoKb2FfdHlwZSA8LSBmaWx0ZXJlZCAlPiUgCiAgZ2dwbG90KGRhdGEgPSBmaWx0ZXJlZCwgbWFwcGluZyA9IGFlcyh4ID0gZmlsdGVyZWQkcHVibGlzaGVyLCBjb2xvdXIgPSBmaWx0ZXJlZCRiYWNrbGlzdF9vYSksIGZpbGwgPSBmaWx0ZXJlZCRiYWNrbGlzdF9vYSkgKwogIGdlb21fYmFyKHBvc2l0aW9uID0gImZpbGwiLCB3aWR0aCA9IDAuNywgZmlsbD0iI0VBRUFFQSIpICsKICBsYWJzKHRpdGxlID0gIkJ1c2luZXNzIE1vZGVsIE9BIGZvciBQdWJsaXNoZXJzIiwgeCA9ICJUb3AgMjUlIG9mIFB1Ymxpc2hlcnMiLCB5ID0gIlByb3BvcnRpb24iLCBjb2xvciA9ICdUeXBlcyBvZiBPQScpICsKICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgwLjc1KSkpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGZ1bmN0aW9uKHgpIHN0cl93cmFwKHN0cl9yZXBsYWNlX2FsbCh4LCAiZm9vIiwgIiAiKSwgd2lkdGggPSAxNykpICsKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIikKCmdncGxvdGx5KG9hX3R5cGUpCgojIGNyb3NzdGFsazoKZnQgPC0gaGlnaGxpZ2h0X2tleShmaWx0ZXJlZCkKb2FfZnQgPC0gZ2dwbG90KGRhdGEgPSBmdCwgbWFwcGluZyA9IGFlcyh4ID0gZnQkcHVibGlzaGVyLCBjb2xvdXIgPSBmdCRiYWNrbGlzdF9vYSksIGZpbGwgPSBmdCRiYWNrbGlzdF9vYSkgKwogIGdlb21fYmFyKHBvc2l0aW9uID0gImZpbGwiLCB3aWR0aCA9IDAuNykgKwogIGxhYnModGl0bGUgPSAiQnVzaW5lc3MgTW9kZWwgT0EgZm9yIFB1Ymxpc2hlcnMiLCB4ID0gIlRvcCAyNSUgb2YgUHVibGlzaGVycyIsIHkgPSAiUHJvcG9ydGlvbiBvZiBCYWNrbGlzdCBPQSIsIGNvbG9yID0gJ1R5cGVzIG9mIE9BJykgKwogIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDAuNzUpKSkgKwogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gZnVuY3Rpb24oeCkgc3RyX3dyYXAoc3RyX3JlcGxhY2VfYWxsKHgsICJmb28iLCAiICIpLCB3aWR0aCA9IDE3KSkKIyBjcm9zc19vYV9mdCA8LSBic2NvbHMoCiMgICBmaWx0ZXJfc2VsZWN0KCJwdWJsaXNoZXIiLCAiU2VsZWN0IGEgcHVibGlzaGVyIiwgZnQsIH5wdWJsaXNoZXIpLAojICAgZ2dwbG90bHkob2FfZnQsIGR5bmFtaWNUaWNrcyA9IFRSVUUpLAojICAgIyB3aWR0aHMgPSBjKDEyLCAxMikKIyApCgojIGJzY29scyhjcm9zc19vYV9mdCkKCgpgYGAKIyMgSWRlYTogUHVibGlzaGVycycgT0EgdnMuIFllYXIKIyMgUXVlc3Rpb246IERpZCBPQSBidXNpbmVzcyBtb2RlbHMgb2YgdGhlIHRvcCAyNSUgcHVibGlzaGVycyBjaGFuZ2UgcGVyIHllYXI/CiMjIE9ic2VydmF0aW9uOgpgYGB7ciAgfQoKb2FfdGltZSA8LSBmdW5jdGlvbihwdWJfbmFtZSkgewogIHB1Yl9mdCA8LSBmaWx0ZXIoZmlsdGVyZWQsIGZpbHRlcmVkJHB1Ymxpc2hlciA9PSBwdWJfbmFtZSkKICAKICBwdWJfb2EgPC0gcHViX2Z0ICU+JSAKICAgIGdncGxvdChkYXRhID0gcHViX2Z0LCBtYXBwaW5nID0gYWVzKHggPSBwdWJfZnQkcGVyaW9kLCBjb2xvdXIgPSBwdWJfZnQkYmFja2xpc3Rfb2EpLCBmaWxsID0gcHViX2Z0JGJhY2tsaXN0X29hKSArCiAgICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJmaWxsIiwgd2lkdGggPSAwLjcsIGZpbGw9IiNFQUVBRUEiKSArCiAgICBsYWJzKHRpdGxlID0gcGFzdGUocHViX25hbWUsICIncyBPQSBUaHJvdWdoIHRoZSBZZWFycyIsIHNlcCA9ICIiKSwgCiAgICAgICAgIHggPSAiWWVhcnMiLCB5ID0gIlByb3BvcnRpb24gb2YgQmFja2xpc3QgT0EiLCBjb2xvciA9ICdUeXBlcyBvZiBPQScpICsKICAgIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDAuNzUpKSkgKwogICAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHM9YygyMDE3LCAyMDE4LCAyMDE5KSkgKwogICAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAiU2V0MSIpCgogIGdncGxvdGx5KHB1Yl9vYSkKICAKfQoKdG9wMjVfbGlzdCA9IGMoInRyYW5zY3JpcHQgVmVybGFnIiwgIkR1a2UgVW5pdmVyc2l0eSBQcmVzcyIsICJVbml2ZXJzaXR5IG9mIE1pY2hpZ2FuIFByZXNzIiwgIk1hbmNoZXN0ZXIgVW5pdmVyc2l0eSBQcmVzcyIsICJQbHV0byBQcmVzcyIsICJMaXZlcnBvb2wgVW5pdmVyc2l0eSBQcmVzcyIpCgpvYV90aW1lKCJ0cmFuc2NyaXB0IFZlcmxhZyIpCgpvYV90aW1lKCJEdWtlIFVuaXZlcnNpdHkgUHJlc3MiKQoKb2FfdGltZSgiVW5pdmVyc2l0eSBvZiBNaWNoaWdhbiBQcmVzcyIpCgpvYV90aW1lKCJNYW5jaGVzdGVyIFVuaXZlcnNpdHkgUHJlc3MiKQoKb2FfdGltZSgiUGx1dG8gUHJlc3MiKQoKb2FfdGltZSgiTGl2ZXJwb29sIFVuaXZlcnNpdHkgUHJlc3MiKQoKYGBgCiMjIElkZWE6IFJldmVudWUgdnMuIE9BCiMjIFF1ZXN0aW9uOiBXaGF0IHRvdGFsIHJldmVudWUgYXJlIHB1Ymxpc2hlcnMgcmVjZWl2aW5nIGVhY2ggeWVhcj8KIyMgT2JzZXJ2YXRpb246IApgYGB7ciAgfQoKIyBGaW5kaW5nIHRvdGFsIHJldmVudWUgZm9yIGVhY2ggcHVibGlzaGVyCgpyZXZlbnVlX2ZpbmRlciA8LSBmdW5jdGlvbihwdWJfbmFtZSkgewogIHB1Yl9maWx0ZXJlZCA8LSBmaWx0ZXIoZmlsdGVyZWQsIGZpbHRlcmVkJHB1Ymxpc2hlciA9PSBwdWJfbmFtZSkKICByZXYgPSBzdW0ocHViX2ZpbHRlcmVkJGV1cm8pCn0KCnJldmVudWVfZGYgPC0gZGF0YS5mcmFtZSgicHVibGlzaGVyIiA9IHRvcDI1X2xpc3QpCnJldmVudWVfbGlzdCA8LSBjKCkKCmZvciAoaSBpbiB0b3AyNV9saXN0KSB7CiAgcmV2ZW51ZV9saXN0PC1jKHJldmVudWVfbGlzdCxyZXZlbnVlX2ZpbmRlcihpKSkKfQoKcmV2ZW51ZV9kZiRyZXZlbnVlIDwtIGMocmV2ZW51ZV9saXN0KQpwcmludChyZXZlbnVlX2RmKQoKIyBnZ3Bsb3Q6CnB1Ymxpc2hlcl9yZXZlbnVlIDwtIHJldmVudWVfZGYgJT4lCiAgZ2dwbG90KGRhdGEgPSByZXZlbnVlX2RmLCBtYXBwaW5nID0gYWVzKHggPSByZXZlbnVlX2RmJHB1Ymxpc2hlciwgeSA9IHJldmVudWVfZGYkcmV2ZW51ZSksIGZpbGwgPSByZXZlbnVlX2RmJHJldmVudWUpICsKICBnZW9tX2NvbCgpICsKICBsYWJzKHRpdGxlID0gIlRvdGFsIFJldmVudWUgZm9yIFB1Ymxpc2hlcnMiLCB4ID0gIlRvcCAyNSUgb2YgUHVibGlzaGVycyIsIHkgPSAiUmV2ZW51ZSAoRXVybykiLCBjb2xvciA9ICdUeXBlcyBvZiBPQScpICsKICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgwLjc1KSkpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGZ1bmN0aW9uKHgpIHN0cl93cmFwKHN0cl9yZXBsYWNlX2FsbCh4LCAiZm9vIiwgIiAiKSwgd2lkdGggPSAxNykpICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDEiKQoKZ2dwbG90bHkocHVibGlzaGVyX3JldmVudWUpCgoKYGBgCiMjIElkZWE6IFJldmVudWUgdnMuIE9BCiMjIFF1ZXN0aW9uOiBXaGF0IHJldmVudWUgYXJlIHB1Ymxpc2hlcnMgcmVjZWl2aW5nIHBlciB5ZWFyPwojIyBPYnNlcnZhdGlvbjogCmBgYHtyICB9CgojIEZpbmRpbmcgdG90YWwgcmV2ZW51ZSBmb3IgZWFjaCBwdWJsaXNoZXIKCnJldmxpc3RfMjAxNyA8LSBjKCkKcmV2bGlzdF8yMDE4IDwtIGMoKQpyZXZsaXN0XzIwMTkgPC0gYygpCgpyZXZsaXN0IDwtIGMoKQoKZm9yIChuYW1lIGluIHRvcDI1X2xpc3QpIHsKICBwdWJfbmFtZSA8LSBmaWx0ZXIoZmlsdGVyZWQsIGZpbHRlcmVkJHB1Ymxpc2hlciA9PSBuYW1lKQogIHJldl8yMDE3ID0gc3VtKHB1Yl9uYW1lW3B1Yl9uYW1lJHBlcmlvZCA9PSAyMDE3LF0kZXVybykKICByZXZsaXN0XzIwMTcgPC0gYyhyZXZsaXN0XzIwMTcsIHJldl8yMDE3KQogIHJldl8yMDE4ID0gc3VtKHB1Yl9uYW1lW3B1Yl9uYW1lJHBlcmlvZCA9PSAyMDE4LF0kZXVybykKICByZXZsaXN0XzIwMTggPC0gYyhyZXZsaXN0XzIwMTgsIHJldl8yMDE4KQogIHJldl8yMDE5ID0gc3VtKHB1Yl9uYW1lW3B1Yl9uYW1lJHBlcmlvZCA9PSAyMDE5LF0kZXVybykgCiAgcmV2bGlzdF8yMDE5IDwtIGMocmV2bGlzdF8yMDE5LCByZXZfMjAxOSkKfQoKcmV2ZW51ZV9kZiA8LSBkYXRhLmZyYW1lKCJwdWJsaXNoZXIiID0gdG9wMjVfbGlzdCkKcmV2ZW51ZV9kZiQnMjAxNycgPC0gYyhyZXZsaXN0XzIwMTcpCnJldmVudWVfZGYkJzIwMTgnIDwtIGMocmV2bGlzdF8yMDE4KQpyZXZlbnVlX2RmJCcyMDE5JyA8LSBjKHJldmxpc3RfMjAxOSkKCnByaW50KHJldmVudWVfZGYpCgpyZXZlbnVlX3llYXIgPC0gYyhyZXZlbnVlX2RmJCcyMDE3JywgcmV2ZW51ZV9kZiQnMjAxOCcsIHJldmVudWVfZGYkJzIwMTknKQp5ZWFyIDwtIGMoJzIwMTcnLCAnMjAxOCcsICcyMDE5JykKCiMgZ2dwbG90OgpwdWJfeWVhcl9yZXZlbnVlMSA8LSByZXZlbnVlX2RmICU+JQogIAogIGdncGxvdChkYXRhID0gcmV2ZW51ZV9kZiwgbWFwcGluZyA9IGFlcyh4ID0gJzIwMTcnLCB5ID0gcmV2ZW51ZV9kZiQnMjAxNycsIGZpbGwgPSByZXZlbnVlX2RmJHB1Ymxpc2hlcikpICsKICBnZW9tX2Jhcihwb3NpdGlvbj0iZG9kZ2UiLCBzdGF0PSJpZGVudGl0eSIpICsKICBsYWJzKHRpdGxlID0gIlRvdGFsIFJldmVudWUgZm9yIFB1Ymxpc2hlcnMiLCB4ID0gIlllYXIiLCB5ID0gIlJldmVudWUgKEV1cm8pIiwgY29sb3IgPSAnUHVibGlzaGVycycpICsKICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgwLjc1KSkpCgpnZ3Bsb3RseShwdWJfeWVhcl9yZXZlbnVlMSkKCnB1Yl95ZWFyX3JldmVudWUyIDwtIHJldmVudWVfZGYgJT4lCiAgCiAgZ2dwbG90KGRhdGEgPSByZXZlbnVlX2RmLCBtYXBwaW5nID0gYWVzKHggPSAnMjAxOCcsIHkgPSByZXZlbnVlX2RmJCcyMDE4JywgZmlsbCA9IHJldmVudWVfZGYkcHVibGlzaGVyKSkgKwogIGdlb21fYmFyKHBvc2l0aW9uPSJkb2RnZSIsIHN0YXQ9ImlkZW50aXR5IikgKwogIGxhYnModGl0bGUgPSAiVG90YWwgUmV2ZW51ZSBmb3IgUHVibGlzaGVycyIsIHggPSAiWWVhciIsIHkgPSAiUmV2ZW51ZSAoRXVybykiLCBjb2xvciA9ICdQdWJsaXNoZXJzJykgKwogIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDAuNzUpKSkKCmdncGxvdGx5KHB1Yl95ZWFyX3JldmVudWUyKQoKcHViX3llYXJfcmV2ZW51ZTMgPC0gcmV2ZW51ZV9kZiAlPiUKICAKICBnZ3Bsb3QoZGF0YSA9IHJldmVudWVfZGYsIG1hcHBpbmcgPSBhZXMoeCA9ICcyMDE5JywgeSA9IHJldmVudWVfZGYkJzIwMTknLCBmaWxsID0gcmV2ZW51ZV9kZiRwdWJsaXNoZXIpKSArCiAgZ2VvbV9iYXIocG9zaXRpb249ImRvZGdlIiwgc3RhdD0iaWRlbnRpdHkiKSArCiAgbGFicyh0aXRsZSA9ICJUb3RhbCBSZXZlbnVlIGZvciBQdWJsaXNoZXJzIiwgeCA9ICJZZWFyIiwgeSA9ICJSZXZlbnVlIChFdXJvKSIsIGNvbG9yID0gJ1B1Ymxpc2hlcnMnKSArCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC43NSkpKQoKZ2dwbG90bHkocHViX3llYXJfcmV2ZW51ZTMpCgoKYGBgCiMjIyBDb250aW51ZWQsIHRyaWVkIHB1dHRpbmcgaXQgaW50byBvbmUgZ3JhcGguIApgYGB7cn0KCnJldmxpc3QgPC0gYygpCnJldmxpc3RfMjAxNyA8LSBjKCkKcmV2bGlzdF8yMDE4IDwtIGMoKQpyZXZsaXN0XzIwMTkgPC0gYygpCgpmb3IgKG5hbWUgaW4gdG9wMjVfbGlzdCkgewogIHB1Yl9uYW1lIDwtIGZpbHRlcihmaWx0ZXJlZCwgZmlsdGVyZWQkcHVibGlzaGVyID09IG5hbWUpCiAgcmV2XzIwMTcgPSBzdW0ocHViX25hbWVbcHViX25hbWUkcGVyaW9kID09IDIwMTcsXSRldXJvKQogIHJldmxpc3RfMjAxNyA8LSBjKHJldmxpc3RfMjAxNywgcmV2XzIwMTcpCiAgcmV2XzIwMTggPSBzdW0ocHViX25hbWVbcHViX25hbWUkcGVyaW9kID09IDIwMTgsXSRldXJvKQogIHJldmxpc3RfMjAxOCA8LSBjKHJldmxpc3RfMjAxOCwgcmV2XzIwMTgpCiAgcmV2XzIwMTkgPSBzdW0ocHViX25hbWVbcHViX25hbWUkcGVyaW9kID09IDIwMTksXSRldXJvKQogIHJldmxpc3RfMjAxOSA8LSBjKHJldmxpc3RfMjAxOSwgcmV2XzIwMTkpCn0KCnJldmxpc3QgPC0gYyhyZXZsaXN0XzIwMTcsIHJldmxpc3RfMjAxOCwgcmV2bGlzdF8yMDE5KQoKcHJpbnQocmV2bGlzdCkKCm5yZXYgPC0gbWF0cml4KHJldmxpc3QsIG5jb2w9NiwgYnlyb3c9VFJVRSkKY29sbmFtZXMobnJldikgPC0gdG9wMjVfbGlzdApyb3duYW1lcyhucmV2KSA8LSBjKCIyMDE3IiwgIjIwMTgiLCAiMjAxOSIpCm5yZXYgPC0gYXMudGFibGUobnJldikKbnJldiA8LSBhcy5kYXRhLmZyYW1lLm1hdHJpeChucmV2KQoKcHJpbnQobnJldikKCiMsIG5yZXYkYER1a2UgVW5pdmVyc2l0eSBQcmVzc2AsIG5yZXYkYFVuaXZlcnNpdHkgb2YgTWljaGlnYW4gUHJlc3NgLCBucmV2JGBQbHV0byBQcmVzc2AsIG5yZXYkYE1hbmNoZXN0ZXIgVW5pdmVyc2l0eSBQcmVzc2AsIG5yZXYkYExpdmVycG9vbCBVbml2ZXJzaXR5IFByZXNzYAoKcHViX3llYXJfcmV2IDwtIG5yZXYgJT4lCiAgCiAgZ2dwbG90KGRhdGEgPSBucmV2LCBtYXBwaW5nID0gYWVzKHggPSBjKCIyMDE3IiwgIjIwMTgiLCAiMjAxOSIpLCB5ID0gYyhucmV2JCJ0cmFuc2NyaXB0IFZlcmxhZyIpLCBmaWxsID0gbnJldiRwdWJsaXNoZXIpKSArCiAgZ2VvbV9iYXIocG9zaXRpb249ImRvZGdlIiwgc3RhdD0iaWRlbnRpdHkiKSArCiAgbGFicyh0aXRsZSA9ICJUb3RhbCBSZXZlbnVlIGZvciBQdWJsaXNoZXJzIiwgeCA9ICJZZWFyIiwgeSA9ICJSZXZlbnVlIChFdXJvKSIsIGNvbG9yID0gJ1B1Ymxpc2hlcnMnKSArCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC43NSkpKQoKZ2dwbG90bHkocHViX3llYXJfcmV2KQoKYGBgCiMjIElkZWE6IERPQUIgYW5hbHlzaXMKIyMgUXVlc3Rpb246IFdoYXQgaXMgdGhlIGF2ZXJhZ2UgdGltZSBnYXAgYmV0d2VlbiB5ZWFyIG9mIHB1YmxpY2F0aW9uIGFuZCBhZGRlZCBvbiBkYXRlPyAKIyMgT2JzZXJ2YXRpb246IApgYGB7cn0KCkRPQUJtZXRhLmRmIDwtIGZpbHRlcihET0FCbWV0YS5kZiwgaXMubmEoRE9BQm1ldGEuZGYkWWVhci5vZi5wdWJsaWNhdGlvbikpCnByaW50KERPQUJtZXRhLmRmJFllYXIub2YucHVibGljYXRpb25bMTo0XSkKZ2FwID0gbWVhbihET0FCbWV0YS5kZiRBZGRlZC5vbi5kYXRlIC0gRE9BQm1ldGEuZGYkWWVhci5vZi5wdWJsaWNhdGlvblsxOjNdKQpwcmludChnYXApCgpgYGAKIyMjIENvbXBhcmlzb24gb2YgY2hhcmdlcyBieSB5ZWFyIGFuZCBiYWNrbGlzdApgYGB7cn0KIyBjcmVhdGUgZmFjZXRlZCBwbG90IG9iamVjdApjaGFyZ2VzLnBsb3QgPC0gS1VicGMuZGYgJT4lIGdncGxvdChhZXMoZXVybykpK2dlb21faGlzdG9ncmFtKGJpbnM9NikrZmFjZXRfZ3JpZChyb3dzPXZhcnMocGVyaW9kKSwgY29scyA9IHZhcnMoYmFja2xpc3Rfb2EpKQoKCiMjIFByZXNlbnQgYXMgU3RhbmRhcmQgcGxvdAogcGxvdChjaGFyZ2VzLnBsb3QpCgojIHRoaXMgcGxvdCB3aWxsIHJlbmRlciBwdWJsaWNseSBodHRwczovL2h0bWxwcmV2aWV3LmdpdGh1Yi5pby8/aHR0cHM6Ly9naXRodWIuY29tL01JVC1JbmZvcm1hdGljcy9tb25vZ3JhcGgvYmxvYi9tYXN0ZXIvMDAlMjBFREElMjBTdGFydC5uYi5odG1sCgpgYGAKIyMjIEludGVyYWN0aXZlIGNoYXJnZXMgZXhwbG9yYXRpb24KYGBge3J9CiBnZ3Bsb3RseShjaGFyZ2VzLnBsb3QpCiMgaHR0cHM6Ly9taXQtaW5mb3JtYXRpY3MuZ2l0aHViLmlvL21vbm9ncmFwaC9kZW1vLmh0bWwKCmBgYApgYGAKIyMjIEludGVyYWN0aXZlIERhdGFzZXQgRXhwbG9yYXRpb24gCmBgYApgYGB7cn0KS1VicGMuZGYgJT4lIEV4UGFuRChkZj0uICAgICAgICx0aXRsZT0iS1UgQm9vayBQcm9jZXNzaW5nIENoYXJnZXMiLGV4cG9ydF9uYl9vcHRpb24gPSBUUlVFKQojIEV4UGFuRCB1c2VzIHNoaW55KCkgd2hpY2ggd29ya3MgcnVubmluZyBSIGxvY2FsbHksIGJ1dCBpc24ndCBnb2luZyB0byB3b3JrIHRocm91Z2ggZ2l0aHViLiBDb3VsZCBwdWJsaXNoIHRocm91Z2ggc2hpbnlhcHBzLmlvIChsb3cgdXNhZ2Ugb25seSksIG9yIGV4cG9ydCAgYSBub24taW50ZXJhY3RpdmUgbm90ZWJvb2sgaXQKIyBzZWU6IGh0dHBzOi8vZHJtYWx0bWFuLnNoaW55YXBwcy5pby9kZW1vLwpgYGAKCg==